// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/renderer/media/aec_dump_message_filter.h"

#include "base/single_thread_task_runner.h"
#include "content/common/media/aec_dump_messages.h"
#include "content/renderer/media/webrtc_logging.h"
#include "ipc/ipc_logging.h"
#include "ipc/ipc_sender.h"

namespace {
const int kInvalidDelegateId = -1;
}

namespace content {

AecDumpMessageFilter* AecDumpMessageFilter::g_filter = NULL;

AecDumpMessageFilter::AecDumpMessageFilter(
    const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
    const scoped_refptr<base::SingleThreadTaskRunner>& main_task_runner)
    : sender_(NULL)
    , delegate_id_counter_(1)
    , io_task_runner_(io_task_runner)
    , main_task_runner_(main_task_runner)
{
    DCHECK(!g_filter);
    g_filter = this;
}

AecDumpMessageFilter::~AecDumpMessageFilter()
{
    DCHECK_EQ(g_filter, this);
    g_filter = NULL;
}

// static
scoped_refptr<AecDumpMessageFilter> AecDumpMessageFilter::Get()
{
    return g_filter;
}

void AecDumpMessageFilter::AddDelegate(
    AecDumpMessageFilter::AecDumpDelegate* delegate)
{
    DCHECK(main_task_runner_->BelongsToCurrentThread());
    DCHECK(delegate);
    DCHECK_EQ(kInvalidDelegateId, GetIdForDelegate(delegate));

    int id = delegate_id_counter_++;
    delegates_[id] = delegate;

    io_task_runner_->PostTask(
        FROM_HERE,
        base::Bind(&AecDumpMessageFilter::RegisterAecDumpConsumer, this, id));
}

void AecDumpMessageFilter::RemoveDelegate(
    AecDumpMessageFilter::AecDumpDelegate* delegate)
{
    DCHECK(main_task_runner_->BelongsToCurrentThread());
    DCHECK(delegate);

    int id = GetIdForDelegate(delegate);
    DCHECK_NE(kInvalidDelegateId, id);
    delegates_.erase(id);

    io_task_runner_->PostTask(
        FROM_HERE,
        base::Bind(&AecDumpMessageFilter::UnregisterAecDumpConsumer, this, id));
}

void AecDumpMessageFilter::Send(IPC::Message* message)
{
    DCHECK(io_task_runner_->BelongsToCurrentThread());
    if (sender_)
        sender_->Send(message);
    else
        delete message;
}

void AecDumpMessageFilter::RegisterAecDumpConsumer(int id)
{
    Send(new AecDumpMsg_RegisterAecDumpConsumer(id));
}

void AecDumpMessageFilter::UnregisterAecDumpConsumer(int id)
{
    Send(new AecDumpMsg_UnregisterAecDumpConsumer(id));
}

bool AecDumpMessageFilter::OnMessageReceived(const IPC::Message& message)
{
    DCHECK(io_task_runner_->BelongsToCurrentThread());
    bool handled = true;
    IPC_BEGIN_MESSAGE_MAP(AecDumpMessageFilter, message)
    IPC_MESSAGE_HANDLER(AecDumpMsg_EnableAecDump, OnEnableAecDump)
    IPC_MESSAGE_HANDLER(AecDumpMsg_DisableAecDump, OnDisableAecDump)
    IPC_MESSAGE_UNHANDLED(handled = false)
    IPC_END_MESSAGE_MAP()
    return handled;
}

void AecDumpMessageFilter::OnFilterAdded(IPC::Channel* channel)
{
    DCHECK(io_task_runner_->BelongsToCurrentThread());
    sender_ = channel;
}

void AecDumpMessageFilter::OnFilterRemoved()
{
    DCHECK(io_task_runner_->BelongsToCurrentThread());

    // Once removed, a filter will not be used again.  At this time the
    // observer must be notified so it releases its reference.
    OnChannelClosing();
}

void AecDumpMessageFilter::OnChannelClosing()
{
    DCHECK(io_task_runner_->BelongsToCurrentThread());
    sender_ = NULL;
    main_task_runner_->PostTask(
        FROM_HERE,
        base::Bind(&AecDumpMessageFilter::DoChannelClosingOnDelegates, this));
}

void AecDumpMessageFilter::OnEnableAecDump(
    int id,
    IPC::PlatformFileForTransit file_handle)
{
    DCHECK(io_task_runner_->BelongsToCurrentThread());
    main_task_runner_->PostTask(
        FROM_HERE, base::Bind(&AecDumpMessageFilter::DoEnableAecDump, this, id, file_handle));
}

void AecDumpMessageFilter::OnDisableAecDump()
{
    DCHECK(io_task_runner_->BelongsToCurrentThread());
    main_task_runner_->PostTask(
        FROM_HERE, base::Bind(&AecDumpMessageFilter::DoDisableAecDump, this));
}

void AecDumpMessageFilter::DoEnableAecDump(
    int id,
    IPC::PlatformFileForTransit file_handle)
{
    DCHECK(main_task_runner_->BelongsToCurrentThread());
    DelegateMap::iterator it = delegates_.find(id);
    if (it != delegates_.end()) {
        it->second->OnAecDumpFile(file_handle);
    } else {
        // Delegate has been removed, we must close the file.
        base::File file = IPC::PlatformFileForTransitToFile(file_handle);
        DCHECK(file.IsValid());
        file.Close();
    }
}

void AecDumpMessageFilter::DoDisableAecDump()
{
    DCHECK(main_task_runner_->BelongsToCurrentThread());
    for (DelegateMap::iterator it = delegates_.begin();
         it != delegates_.end(); ++it) {
        it->second->OnDisableAecDump();
    }
}

void AecDumpMessageFilter::DoChannelClosingOnDelegates()
{
    DCHECK(main_task_runner_->BelongsToCurrentThread());
    for (DelegateMap::iterator it = delegates_.begin();
         it != delegates_.end(); ++it) {
        it->second->OnIpcClosing();
    }
    delegates_.clear();
}

int AecDumpMessageFilter::GetIdForDelegate(
    AecDumpMessageFilter::AecDumpDelegate* delegate)
{
    DCHECK(main_task_runner_->BelongsToCurrentThread());
    for (DelegateMap::iterator it = delegates_.begin();
         it != delegates_.end(); ++it) {
        if (it->second == delegate)
            return it->first;
    }
    return kInvalidDelegateId;
}

} // namespace content
